/**************************************************************************************
* Cell.java
* 
*************************************************************************************/
package creid.mythos.engine;

import java.io.Serializable;

public class Cell extends Entity implements Serializable
{
	//Constants/////////////////////////////////////////////////////////////////////////
	
	public static final int NO_OP = -1;
	
	public static final char[] UNEXPLORED_CELL = {' ', 'k'};
	
	//Attributes////////////////////////////////////////////////////////////////////////

	private static final long serialVersionUID = 8155672442470041455L;
	
	private GameMap map;
	private int x;
	private int y;
	
	private boolean seen;
	
	private boolean passable;
	private boolean swimmable;
	private boolean flyable;
	private boolean slow;
	private boolean cover;
	private boolean elevated;
	private boolean opaque;
	private boolean room; //has room for itmes
	
	//Operation code
	private int operation;
	private int terrainType;
	private String opMessage;
	
	private char[] showSprite;
	private String showName;
	
	private Actor occupant;
	
	private Item treasure;
	
	//Djisktra Map data
	private int[] dMap;	//distance from player/allies. Used for mob pathing.
	private boolean DMapped;
	
	private boolean reserved;
	
	private char[] animation;
	
	//Constructors//////////////////////////////////////////////////////////////////////

	public Cell(String name, char[] sprite, boolean passable, boolean swimmable, boolean flyable, boolean slow, boolean cover, 
			boolean elevated, boolean opaque, boolean room, int operation, String opMessage, int terrainType)
	{
		super(-1, name, null, sprite, 0, Entity.NEVER_GEN);
		
		this.passable = passable;
		this.swimmable = swimmable;
		this.flyable = flyable;
		this.slow = slow;
		this.cover = cover;
		this.elevated = elevated;
		this.opaque = opaque;
		this.operation = operation;
		this.room = room;
		this.opMessage = opMessage;
		this.reserved = false;
		this.terrainType = terrainType;
		
		map=null;
		x = -1;
		y = -1;
		
		//This constructor should only be used for terrain objects
		seen = true;
		showSprite = sprite;
		showName = super.getSingularName();
		DMapped = true;
		dMap = null;
		animation = null;
	}
	
	//Regular cell constructor
	public Cell(GameMap map, int x, int y, Cell terrain)
	{
		super(-1, terrain.getSingularName(), null, terrain.getSprite(), 0, Entity.NEVER_GEN);
		setTerrain(terrain);
		
		this.x = x;
		this.y = y;
		this.map = map;

		//Unxeplored
		showSprite = UNEXPLORED_CELL;
		showName = "an unexplored place";
		seen = false;
		
		//Dijkstra Map stuff
		DMapped = false;
		dMap = new int[GameMap.DMAP_ARRAY_SIZE];
		for (int i = 0; i < dMap.length; i++)
			dMap[i] = GameMap.UNSET;
		
		animation = null;
		reserved = false;
	}
	
	//Methods///////////////////////////////////////////////////////////////////////////
	
	public int getTerrainType()
	{
		return terrainType;
	}
	
	public int getX()
	{
		return x;
	}

	public void setX(int x)
	{
		this.x = x;
	}

	public int getY()
	{
		return y;
	}

	public boolean isReserved()
	{
		return reserved;
	}
	
	public void setReserved(boolean reserved)
	{
		this.reserved = reserved;
	}

	public void setY(int y)
	{
		this.y = y;
	}
	
	public boolean isPassable(boolean walk, boolean swim, boolean fly)
	{
		if (fly && flyable)
			return true;
		
		if (walk && passable)
			return true;
		
		if (swim && swimmable)
			return true;
		
		return false;
	}
	
	public boolean isPassable()
	{
		return passable;
	}
	
	public boolean hasRoom()
	{
		return room;
	}
	
	public void setRoom(boolean room)
	{
		this.room = room;
	}

	public void setPassable(boolean passable)
	{
		this.passable = passable;
	}

	public boolean isSwimmable()
	{
		return swimmable;
	}

	public void setSwimmable(boolean swimmable)
	{
		this.swimmable = swimmable;
	}

	public boolean isSolid()
	{
		return flyable;
	}

	public void setFlyable(boolean flyable)
	{
		this.flyable = flyable;
	}

	public boolean isSlow()
	{
		return slow;
	}

	public void setSlow(boolean slow)
	{
		this.slow = slow;
	}

	public boolean isCover()
	{
		return cover;
	}

	public void setCover(boolean cover)
	{
		this.cover = cover;
	}

	public boolean isElevated()
	{
		return elevated;
	}

	public void setElevated(boolean elevated)
	{
		this.elevated = elevated;
	}

	public boolean isOpaque()
	{
		return opaque;
	}

	public void setOpaque(boolean opaque)
	{
		this.opaque = opaque;
	}

	public boolean isOperable()
	{
		return (operation != NO_OP);
	}

	public int getOperation()
	{
		return operation;
	}
	
	public void setOperation(int operation)
	{
		this.operation = operation;
	}
	
	public String getOpMessage()
	{
		return opMessage;
	}

	void setTerrain(Cell terrain)
	{
		terrainType = terrain.terrainType;
		setSingularName(terrain.getSingularName());
		setCover(terrain.isCover());
		setElevated(terrain.isElevated());
		setFlyable(terrain.isSolid());
		setOpaque(terrain.isOpaque());
		setOperation(terrain.getOperation());
		setPassable(terrain.isPassable());
		setRoom(terrain.hasRoom());
		setSlow(terrain.isSlow());
		setSwimmable(terrain.isSwimmable());
		opMessage = terrain.opMessage;
		setSprite(terrain.getSprite());
		if (seen)
		{
			showSprite = getSprite();
			getName(INDEFINITE);
		}	
	}

	public int getDMap(int type)
	{
		return dMap[type];
	}

	public void setDMap(int type, int value)
	{
		dMap[type] = value;
	}
	
	@Override
	public char[] getSprite()
	{
		if (isSeen() || Mythos.DEBUG_MAP)
		{
			//If we are drawing an animation, do so
			if (animation != null)
				return animation;
			
			//Update our sprite
			if (isOccupied())
				showSprite = getOccupant().getSprite();
			else if (hasTreasure())
				showSprite = getTreasure().getSprite();
			else
				showSprite = super.getSprite(); //Terrain sprite
		}
		
		return showSprite;
	}

	public Actor getOccupant()
	{
		return occupant;
	}

	public boolean setOccupant(Actor occupant)
	{
		if (isOccupied() && occupant != null)
			return false;
		
		this.occupant = occupant;
		return true;
	}
	
	public boolean isOccupied()
	{
		if (getOccupant() == null)
			return false;
		else
			return true;
	}
	
	public boolean canOccupy(Actor a)
	{
		if (!isPassable() || (isOccupied() && occupant != a)) //We can occupy the cell we are in
			return false;
		
		return true;
	}

	public boolean isSeen()
	{
		return seen;
	}

	public void setSeen(boolean seen)
	{
		this.seen = seen;
	}
	
	
	@Override
	public String getName(boolean article)
	{
		if (isSeen() || Mythos.DEBUG_MAP)
		{
			boolean complex = false;
			
			StringBuilder name = new StringBuilder();
			
			if (isOccupied())
			{
				complex = true;
				name.append(occupant.getName(INDEFINITE));
			}
			
			if (hasTreasure())
			{
				if (complex)
					name.append(" on ");
	
				complex = true;
				name.append(treasure.getName(INDEFINITE));
			}
			
			//Stand on a floor, in cover, liquid, or a wall
			if (complex)
				if (isPassable() && !isCover())
					name.append(" on ");
				else 
					name.append(" in ");
			
			name.append(super.getName(INDEFINITE));
			
			showName = name.toString();
		}
	
		return showName;
	}

	public Item getTreasure()
	{
		return treasure;
	}

	public void setTreasure(Item treasure)
	{
		this.treasure = treasure;
	}
	
	public boolean hasTreasure()
	{
		if (treasure == null)
			return false;
		else
			return true;
	}
	
	public boolean canHoldTreasure()
	{
		if (hasTreasure())
			return false;
		
		return hasRoom();
	}
	
	public Item takeTreasure()
	{
		Item i = treasure;
		treasure = null;
		return i;
	}

	public GameMap getMap()
	{
		return map;
	}

	public void setMap(GameMap map)
	{
		this.map = map;
	}

	public boolean isDMapped()
	{
		return DMapped;
	}

	public void setDMapped(boolean dMapped)
	{
		DMapped = dMapped;
	}
	
	boolean operate(int skill)
	{
		if (operation == NO_OP)
			return false;
		
		//TODO: Add locks and difficulty in unlocking (with skill representing power to unlock.)
		boolean success = true;
		
		if (success)
		{
			//Default operation is to change to a different terrain type, with ID=operation value
			if (operation > -1)
			{
				//Change the cell
				setTerrain(Mythos.TerrainTypes[operation]);
			}
			//Else opcode is a specific function to execute.
		}
		
		return success;
	}

	public char[] getAnimation()
	{
		return animation;
	}

	public void setAnimation(char[] animation)
	{
		this.animation = animation;
	}
}